#!/usr/bin/env bash
# ******************************************************
# Filename     :	rsync.sh
# Last modified:	2017-07-04 14:08
# Version      : 
# Author       : jack.zang
# Email        : jack.zang@aishangwei.net
# Description  : 1. 前提需安装 inotify-tools 到 /usr/local目录下
#                2. 如果需要同步到多台服务器上，可以启用下面的ip2，并启用下面判断句中的 inotify
#                3. 因为inotify只在启动时会监控目录，没有启动期间的文件发生更改，它是不知道的，所以
#                  可以在定时任务里面，添加一个每2小时做1次全量同步，防止各种意外遗漏，保证目录一致
#                4. 该脚本可以使用 nohup 放在后台运行，并放在开机启动里面
# ******************************************************

# 需要同步的源路径
src=/data/www                           
# 目标服务器上 rsync --daemon 发布的名称，也就是自定义模块名
des=testcom    
# rsync服务器定义的密码文件
rsync_passwd_file=/etc/rsyncd.passwd           
# 目标服务器1
ip1=172.16.1.12                 
# 目标服务器2
#ip2=172.16.1.13
# rsync服务器定义的用户名                 
user=backuser                  
# log 目录
rsynclog=/data/logs/rsync.log

# 检测命令是否存在
[ ! -e /usr/bin/inotifywait ] && yum -y install inotify-tools

# 此方法中，由于rsync同步的特性，这里必须要先cd到源目录，inotify再>监听 ./ 
# 才能rsync同步后目录结构一致，有兴趣的同学可以进行各种尝试观看其效果
cd ${src}  

# 把监控到有发生更改的"文件路径列表"循环                            
/usr/bin/inotifywait -mrq --format  '%Xe %w%f' -e modify,create,delete,attrib,close_write,move ./ | while read file        
do
       # 把inotify输出切割 把事件类型部分赋值给INO_EVENT
        INO_EVENT=$(echo $file | awk '{print $1}')      
       # 把inotify输出切割 把文件路径部分赋值给INO_FILE
        INO_FILE=$(echo $file | awk '{print $2}')       
        echo "-------------------------------$(date)------------------------------------"
        echo $file

        # 增加、修改、写入完成、移动进事件
        # 增、改放在同一个判断，因为他们都肯定是针对文件的操作，即使是新建目录，
        # 要同步的也只是一个空目录，不会影响速度。
        # 判断事件类型
        if [[ $INO_EVENT =~ 'CREATE' ]] || [[ $INO_EVENT =~ 'MODIFY' ]] || [[ $INO_EVENT =~ 'CLOSE_WRITE' ]] || [[ $INO_EVENT =~ 'MOVED_TO' ]]         
        then
                echo 'CREATE or MODIFY or CLOSE_WRITE or MOVED_TO'
                rsync -avzcR --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip1}::${des}             # INO_FILE变量代表路径哦  -c校验文件内容
                #rsync -avzcR --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip2}::${des}
                 # 仔细看 上面的rsync同步命令 源是用了$(dirname ${INO_FILE})变量 即每次只针对性的同步发生
                 # 改变的文件的目录(只同步目标文件的方法在生产环境的某些极端环境下会漏文件 现在可以在不
                 # 漏文件下也有不错的速度 做到平衡) 然后用-R参数把源的目录结构递归到目标后面 保证
                 # 目录结构一致性,两条语句之间可以使用  && 连接
         fi

        # 删除、移动出事件
        if [[ $INO_EVENT =~ 'DELETE' ]] || [[ $INO_EVENT =~ 'MOVED_FROM' ]]
        then
                echo 'DELETE or MOVED_FROM'
                rsync -avzR --delete --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip1}::${des} 
                #rsync -avzR --delete --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip2}::${des}
                # 看rsync命令 如果直接同步已删除的路径${INO_FILE}会报no such or directory错误 所以这里同
                # 步的源是被删文件或目录的上一级路径，并加上--delete来删除目标上有而源中没有的文件，这里
                # 不能做到指定文件删除，如果删除的路径越靠近根，则同步的目录月多，同步删除的操作就越花时
                # 间。这里有更好方法的同学，欢迎交流。
        fi

        # 修改属性事件 指 touch chgrp chmod chown等操作
        if [[ $INO_EVENT =~ 'ATTRIB' ]]
        then
                echo 'ATTRIB'
                # 如果修改属性的是目录 则不同步，因为同步目录会发>生递归扫描，等此目录下的文件发生同步
                # 时，rsync会顺带更新此目录。
                if [ ! -d "$INO_FILE" ]                 
                then
                        rsync -avzcR --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip1}::${des}             
                       #rsync -avzcR --password-file=${rsync_passwd_file} $(dirname ${INO_FILE}) ${user}@${ip2}::${des}
                fi
        fi
done >> $rsynclog
